
/**
 *	\file Definition of the nmn::hashed_string class.
 *	\author Sean Tromans
 */

#ifndef	NMN_HASHED_STRING_HPP
#define	NMN_HASHED_STRING_HPP

#include <boost/functional/hash.hpp>
#include <string>

namespace nmn
{
	namespace detail
	{
		/**
		Tag to allow the hashed string to use lazy equality checking by default.
		\author Sean Tromans
		*/
		struct use_lazy_equality { };

		/**
		Tag to force the hashed string to use strict equality checking.
		\author Sean Tromans
		*/
		struct use_strict_equality { };
	}

	/**
	 *	Class to help with improving the performance of strings as ID's.
	 *	Pre-hashes the string to allow for the same hash result to be used in multiple queries.
	 *	\author Sean Tromans
	 */
	template < typename TString, typename TEqualityTag = detail::use_lazy_equality, typename THashFunctor = boost::hash< TString > >
	class basic_hashed_string
	{
		//
		//	Public Member Types
		//
		public:

			typedef basic_hashed_string< TString, TEqualityTag > my_type;

			typedef	TString				string_type;
			typedef	string_type &		string_reference;
			typedef	string_type const & string_const_reference;

			typedef THashFunctor		hash_functor;

		//
		//	Public Member Functions
		//
		public:

			/**
			 *	Default Constructor.
			 *	\author Sean Tromans
			 */
			basic_hashed_string( )
				:		value()
					,	h( 0 )
			{
				compute_hash( );
			}

			/**
			 *	Implicit constructor from a string value.
			 *	@param v The string value to store.
			 *	\author Sean Tromans
			 */
			basic_hashed_string( string_const_reference v )
				:		value( v )
					,	h( 0 )
			{
				compute_hash( );
			}

			/**
			 *	Implicit constructor from a c-string value.
			 *	@param v The string value to store.
			 *	\author Sean Tromans
			 */
			basic_hashed_string( typename string_type::value_type const * const v )
				:		value( v )
					,	h( 0 )
			{
				compute_hash( );
			}

			/**
			 *	Copy Constructor.
			 *	@param rhs The other hashed_string to copy from.
			 *	\author Sean Tromans
			 */
			basic_hashed_string( basic_hashed_string const & rhs )
				:		value( rhs.value )
					,	h( rhs.h )
			{
			}

			/**
			 *	Assignment from a string value.
			 *	@param rhs The string to assign from.
			 *	\author Sean Tromans
			 */
			basic_hashed_string &	operator = ( string_const_reference rhs )
			{
				value = rhs;
				compute_hash( );
				return *this;
			}

			/**
			 *	Assignment from a string value.
			 *	@param rhs The string to assign from.
			 *	\author Sean Tromans
			 */
			basic_hashed_string &	operator = ( typename string_type::value_type const * const rhs )
			{
				value = rhs;
				compute_hash( );
				return *this;
			}

			/**
			 *	Assignment from another hashed_string.
			 *	@param rhs The hashed_string to assign from.
			 *	\author Sean Tromans
			 */
			basic_hashed_string &	operator = ( basic_hashed_string const & rhs )
			{
				value = rhs.value;
				h = rhs.h;
				return *this;
			}

			/**
			 *	Implicit conversion to a string type.
			 *	\author Sean Tromans
			 */
			operator string_const_reference ( ) const
			{
				return str( );
			}

			/**
			 *	Gets a reference to the string value.
			 *	\author Sean Tromans
			 */
			string_const_reference str( ) const
			{
				return value;
			}

			/**
			 *	Gets a reference to the hash value of this hashed_string.
			 *	\author Sean Tromans
			 */
			size_t	hash( ) const
			{
				return h;
			}

			/**
			 *	Strict equality check which compares the raw string value.
			 *	\author Sean Tromans
			 */
			template < typename TOtherEqualityType >
			bool strict_equals( basic_hashed_string< TString, TOtherEqualityType, THashFunctor > const & rhs ) const
			{
				return str( ) == rhs.str( );
			}

		//
		//	Private Member Types
		//
		private:

		//
		//	Private Member Functions
		//
		private:

			/**
			 *	Calculates and stores the hash of the string.
			 *	\author Sean Tromans
			 */
			void	compute_hash( )
			{
				THashFunctor string_hasher;
				h = string_hasher( value );
			}

		//
		//	Private Member Variables
		//
		private:

			string_type	value;	///< The string value to be hashed.
			size_t		h;		///< The hash result.
	};

	/**
	*	Test for equality between two hashed_strings using lazy equality.
	*	@param rhs The hashed_string to test against.
	*	\author Sean Tromans
	*/
	template < typename TString, typename THashFunctor >
	inline bool operator == ( basic_hashed_string<TString, detail::use_lazy_equality, THashFunctor> const & lhs, basic_hashed_string<TString, detail::use_lazy_equality, THashFunctor> const & rhs )
	{
		return lhs.hash( ) == rhs.hash( );
	}

	/**
	*	Test for equality between two hashed_strings using strict equality.
	*	@param rhs The hashed_string to test against.
	*	\author Sean Tromans
	*/
	template < typename TString, typename TEqualityTypeA, typename TEqualityTypeB, typename THashFunctor >
	inline bool operator == ( basic_hashed_string<TString, TEqualityTypeA, THashFunctor> const & lhs, basic_hashed_string<TString, TEqualityTypeB, THashFunctor> const & rhs )
	{
		return lhs.strict_equals( rhs );
	}

	/**
	*	Test for inequality between two hashed_strings.
	*	@param rhs The hashed_string to test against.
	*	\author Sean Tromans
	*/
	template < typename TString, typename TEqualityTypeA, typename TEqualityTypeB, typename THashFunctor >
	inline bool operator != ( basic_hashed_string<TString, TEqualityTypeA, THashFunctor> const & lhs, basic_hashed_string<TString, TEqualityTypeB, THashFunctor> const & rhs )
	{
		return !( lhs == rhs );
	}

	/**
	 *	The hash value for a hashed_string is simply the pre-computed hash stored within.
	 *	@param s The hashed_string being hashed.
	 *	\author Sean Tromans
	 */
	template < typename TString, typename TEqualityType, typename THashFunctor >
	inline std::size_t hash_value( basic_hashed_string<TString, TEqualityType, THashFunctor> const & s )
    {
		return s.hash( );
    }

	/**
	 *	Output stream for a hashed_string.
	 *	@param oss The output stream.
	 *	@param rhs The hashed_string to output.
	 *	\author Sean Tromans
	 */
	template < typename Elem, typename Traits, typename TString, typename TEqualityType, typename THashFunctor >
	inline std::basic_ostream< Elem, Traits > & operator << ( std::basic_ostream< Elem, Traits > & oss, basic_hashed_string<TString, TEqualityType, THashFunctor> const & rhs )
	{
		oss << "{ " << rhs.str( ) << " , " << rhs.hash( ) << " }";
		return oss;
	}

#if !defined(NMN_USE_HASHED_STRICT_EQUALITY) || NMN_USE_HASHED_STRICT_EQUALITY == 0
	typedef	basic_hashed_string< std::string, detail::use_lazy_equality >		hashed_string;
	typedef	basic_hashed_string< std::wstring, detail::use_lazy_equality >		hashed_wstring;
	typedef	basic_hashed_string< std::u16string, detail::use_lazy_equality >	hashed_u16string;
	typedef	basic_hashed_string< std::u32string, detail::use_lazy_equality >	hashed_u32string;
#else
	typedef	basic_hashed_string< std::string, detail::use_strict_equality >		hashed_string;
	typedef	basic_hashed_string< std::wstring, detail::use_strict_equality >	hashed_wstring;
	typedef	basic_hashed_string< std::u16string, detail::use_strict_equality >	hashed_u16string;
	typedef	basic_hashed_string< std::u32string, detail::use_strict_equality >	hashed_u32string;
#endif

}

#endif	//NMN_HASHED_STRING_HPP
